package com.qiaofang.applet.service.other.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.google.common.collect.Lists;
import com.mongodb.client.result.UpdateResult;
import com.qiaofang.applet.common.base.MongoBaseService;
import com.qiaofang.applet.common.context.QfContextHolder;
import com.qiaofang.applet.common.exception.BusinessException;
import com.qiaofang.applet.common.util.RedisKeyUtil;
import com.qiaofang.applet.common.util.RedisUtil;
import com.qiaofang.applet.service.other.domain.*;
import com.qiaofang.applet.service.other.repository.CityCompanyRepository;
import com.qiaofang.applet.service.other.repository.CorporationRepository;
import com.qiaofang.applet.service.other.repository.ProvinceCityRepository;
import com.qiaofang.applet.service.other.service.CityCompanyService;
import com.qiaofang.applet.service.other.service.dto.*;
import com.qiaofang.applet.service.other.service.mapstruct.CityCompanyMapper;
import com.qiaofang.applet.service.other.service.mapstruct.ProvinceCityMapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.springframework.data.domain.Sort.Direction.DESC;

/**
 * Created by IDEA.
 *
 * @auther Zym.
 * @company qiaofang
 * @date 2020/9/15 13:50
 */

@Service
@Slf4j
public class CityCompanyServiceImpl extends MongoBaseService<CityCompany, String> implements CityCompanyService {

    private final static Logger LOGGER = LoggerFactory.getLogger(CityCompanyServiceImpl.class);

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private CityCompanyRepository repository;

    @Autowired
    private CorporationRepository corporationRepository;

    @Autowired
    private CityCompanyMapper cityCompanyMapper;

    @Autowired
    private ProvinceCityRepository provinceCityRepository;

    @Autowired
    private ProvinceCityMapper provinceCityMapper;

    @Autowired
    private RedisUtil redisUtil;

    public CityCompanyServiceImpl(CityCompanyRepository repository) {
        super(repository);
        this.repository = repository;
    }

    @Override
    public void saveCompany(CityCompany cc) {
        String id = cc.getId();
        String companyUuid = cc.getCompanyUuid();

        //同一个城市库下，只能有一个主城市库(0527修改，这里需要判空 公司介绍这里flag没有值)
        if (cc.getMainCityFlag() != null && cc.getMainCityFlag() == 1) {
            List<CityCompany> cityCompanyList = repository.findAllByCorporationCodeAndCityCodeAndMainCityFlag(cc.getCorporationCode(), cc.getCityCode(), 1);
            if (!CollectionUtils.isEmpty(cityCompanyList) && !org.apache.commons.lang3.StringUtils.equals(cityCompanyList.get(0).getCompanyUuid(), cc.getCompanyUuid())) {
                throw new BusinessException("同一集团下，同一个主城市库只能有一个");
            }
        }

        if (StringUtils.isEmpty(id)) {
            CityCompany cityCompany = repository.findOneByCompanyUuid(companyUuid);
            Corporation corporation = corporationRepository.findOneByCorporationCode(cc.getCorporationCode());
            if (corporation == null) {
                LOGGER.error("集团编码:{}不存在，不可新增", cc.getCorporationCode());
                throw new BusinessException("公司编码不存在，不可新增");
            }
            if (cityCompany != null) {
                LOGGER.error("公司uuid:{}已存在于集团:{}之下，不可新增", companyUuid, cityCompany.getCorporationCode());
                throw new BusinessException("公司uuid已存在，不可新增");
            }
            cityCompany = new CityCompany();
            cityCompany.setCorporationCode(cc.getCorporationCode());
            cityCompany.setCompanyUuid(companyUuid);
            cityCompany.setCompanyName(cc.getCompanyName());
            cityCompany.setCityCode(cc.getCityCode());
            cityCompany.setCompanyCity(cc.getCompanyCity());
            cityCompany.setDefaultFlag(cc.getDefaultFlag());
            cityCompany.setStatus(cc.getStatus());
            cityCompany.setInitFlag(true);
            cityCompany.setMainCityFlag(cc.getMainCityFlag());
            cityCompany.setSubWebDomain(cc.getSubWebDomain());
            cityCompany.setUniverseCityId(cc.getUniverseCityId());
            if (corporation.getV20Flag()){
                cityCompany.setPropertyShowRules(initCityCompanyEmpShowList());
            }
            repository.insert(cityCompany);
        } else {
            CityCompany cityCompany = repository.findOneByCompanyUuid(companyUuid);
            cc.setPropertyShowRules(cityCompany.getPropertyShowRules());
            cc.setFirsthandShowRules(cityCompany.getFirsthandShowRules());
            updateById(cc);
        }
        clearDomainCache(cc.getCorporationCode());
    }

    /**
     * 获取公司列表
     *
     * @param corporationCode
     * @return
     */
    @Override
    public List<CityCompanyDto> getCompanyList(String corporationCode) {
        return cityCompanyMapper.toDto(repository.findAllByCorporationCode(corporationCode));
    }

    /**
     * 获取默认城市公司
     *
     * @param corporationCode
     * @return
     */
    @Override
    public CityCompany getDefaultCityCompany(String corporationCode) {
        List<CityCompany> cityCompanyList = getCitys(corporationCode);
        return !CollectionUtils.isEmpty(cityCompanyList) ? cityCompanyList.get(0) : null;
    }

    /**
     * 获取城市列表
     *
     * @param corporationCode
     * @return
     */
    @Override
    public List<CityDesDto> getCityList(String corporationCode) {
        List<CityDesDto> cityDesDtos = new ArrayList<>();
        List<CityCompany> cityCompanyList = getCitys(corporationCode);
        if(!CollectionUtils.isEmpty(cityCompanyList)) {
            CityDesDto cityDesDto;
            for(CityCompany cityCompany : cityCompanyList) {
                cityDesDto = new CityDesDto();
                cityDesDto.setCityCode(cityCompany.getCityCode());
                cityDesDto.setCityName(cityCompany.getCompanyCity());
                cityDesDto.setSubWebDomain(cityCompany.getSubWebDomain());
                cityDesDto.setDefaultFlag(cityCompany.getDefaultFlag());
                cityDesDto.setUniverseCityId(cityCompany.getUniverseCityId());
                cityDesDtos.add(cityDesDto);
            }
        }
        return cityDesDtos;
    }

    @Override
    public List<CityDesDto> getCityList(String corporationCode, List<String> cityCodeList) {
        Criteria criteria = Criteria.where("corporationCode").is(corporationCode)
                .and("cityCode").in(cityCodeList);
//                .and("status").is(1);
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("cityCode").first("cityCode").as("cityCode")
                        .first("companyCity").as("cityName")
                        .first("defaultFlag").as("defaultFlag"),
                Aggregation.sort(DESC, "defaultFlag", "cityCode")
        );

        AggregationResults result = mongoTemplate.aggregate(agg, "other_city_company", CityDesDto.class);
        return result.getMappedResults();
    }

    private List<CityDesDto> transBean2List(Object obj) {
        return JSONObject.parseArray(JSONObject.toJSONString(obj), CityDesDto.class);
    }

    @Override
    public void removeCompanyByCorp(String corporationCode) {
        repository.deleteAllByCorporationCode(corporationCode);
        clearDomainCache(corporationCode);
    }

    @Override
    public Map<String, String> getCityMap(String corporationCode) {
        Map<String, String> cityMap = new HashMap<>();
        List<CityDesDto> cityDesDtos = getCityList(corporationCode);
        if (CollectionUtils.isEmpty(cityDesDtos)) {
            return cityMap;
        }

        for (CityDesDto cityDesDto : cityDesDtos) {
            cityMap.put(cityDesDto.getCityCode(), cityDesDto.getCityName());
        }

        return cityMap;
    }

    @Override
    public CityCompany getCompanyByUuid(String companyUuid) {
        return repository.findOneByCompanyUuid(companyUuid);
    }

    @Override
    public String getCompanyUuid(String cityCode, String corporationCode) {
        List<CityCompany> companies = repository.findCityCompaniesByCityCodeAndCorporationCode(cityCode, corporationCode);
        if (CollectionUtils.isEmpty(companies)) {
            return "";
        }
        return companies.get(0).getCompanyUuid();
    }

    @Override
    public CityCompany getCompanyByCityAndCorp(String cityCode, String corporationCode) {
        List<CityCompany> companies = repository.findCityCompaniesByCityCodeAndCorporationCode(cityCode, corporationCode);
        if (CollectionUtils.isEmpty(companies)) {
            return new CityCompany();
        }
        return companies.get(0);
    }

    @Override
    public List<CompanyEmpShowDto> getEmpShowList(String corporationCode, List<String> cityCodes) {
        List<CompanyEmpShowDto> results = new LinkedList<>();
        Query query = new Query(Criteria.where("corporationCode").is(corporationCode));
        query.addCriteria(Criteria.where("status").is(1));
        if(!CollectionUtils.isEmpty(cityCodes)){
            query.addCriteria(Criteria.where("cityCode").in(cityCodes));
        }
        List<CityCompany> list = mongoTemplate.find(query, CityCompany.class);
        for (CityCompany cityCompany : list) {
            results.add(CompanyEmpShowDto.builder()
                    .companyCity(cityCompany.getCompanyCity())
                    .companyName(cityCompany.getCompanyName())
                    .companyUuid(cityCompany.getCompanyUuid())
                    .propertyShowRulesStr(getShowRulesStr(cityCompany.getPropertyShowRules()))
                    .firsthandShowRulesStr(getShowRulesStr(cityCompany.getFirsthandShowRules()))
                    .build());

        }
        return results;
    }

    @Override
    public String getCityByCitycode(String cityCode) {
        List<CityDesDto> cityList = getCityList(QfContextHolder.getContext().getCorporationCode());
        for (CityDesDto cityDesDto : cityList) {
            if (cityDesDto.getCityCode().equals(cityCode)) {
                return cityDesDto.getCityName();
            }
        }
        return null;
    }

    private String getShowRulesStr(EmpShowRules empShowRules) {
        log.info("处理房源规则数据展示:{}", JSON.toJSONString(empShowRules));
        StringBuilder sb = new StringBuilder();
        if (empShowRules.getShowStrategy() == 1) {
            sb.append("按房源角色展示-");
            List<PropertyRole> roleList = empShowRules.getRoleList();
            if (roleList.size() > 0) {
                int index = 0;
                for (PropertyRole propertyRole : roleList) {
                    if ((!propertyRole.isFixedShow() && !propertyRole.isShowOrHide()) || index >= 5) {
                        continue;
                    }
                    index++;
                    sb.append(propertyRole.getRoleName() + (index == 5 ? "... " : "、"));
                }
            }
        } else {
            sb.append("按指定规则展示-");
            AssignRule assignRule = empShowRules.getAssignRule();
            int sortRule = assignRule.getSortRule();
            int showNum = assignRule.getShowNum();
            switch (sortRule) {
                case 1:
                    sb.append("按经纪人上架到个人门店时间的顺序 最多展示" + showNum + "个经纪人 ");
                    break;
                case 2:
                    sb.append("按经纪人带看次数的顺序 最多展示" + showNum + "个经纪人 ");
                    break;
                case 3:
                    sb.append("按经纪人跟进次数的顺序 最多展示" + showNum + "个经纪人 ");
                    break;
                case 4:
                    sb.append("按经纪人实勘次数的顺序 最多展示" + showNum + "个经纪人 ");
                    break;
            }
        }
        log.info("处理房源规则数据结果:{}", sb);
        return sb.subSequence(0, sb.length() - 1).toString();
    }

    @Override
    public CompanyEmpRulesDto getEmpShowRules(String companyUuid, String corporationCode) {
        CityCompany cityCompany = repository.findOneByCompanyUuid(companyUuid);
        if (cityCompany == null) {
            throw new BusinessException("获取公司信息失败");
        }
        Corporation corporation = corporationRepository.findOneByCorporationCode(corporationCode);
        if (corporation != null && corporation.getV20Flag()) {
            return CompanyEmpRulesDto.builder()
                    .companyUuid(cityCompany.getCompanyUuid())
                    .firsthandShowRules(cityCompany.getPropertyShowRules())
                    .propertyShowRules(cityCompany.getPropertyShowRules())
                    .build();
        } else {
            return CompanyEmpRulesDto.builder()
                    .companyUuid(cityCompany.getCompanyUuid())
                    .firsthandShowRules(cityCompany.getFirsthandShowRules())
                    .propertyShowRules(cityCompany.getPropertyShowRules())
                    .build();
        }
    }

    @Override
    public void saveEmpShowRules(CompanyEmpRulesDto rulesDto) {
        if (rulesDto == null || StringUtils.isEmpty(rulesDto.getCompanyUuid()) ||
                rulesDto.getPropertyShowRules() == null || rulesDto.getFirsthandShowRules() == null) {
            return;
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("companyUuid").is(rulesDto.getCompanyUuid()));
        Update update = new Update();
        update.set("propertyShowRules", rulesDto.getPropertyShowRules());
        update.set("firsthandShowRules", rulesDto.getFirsthandShowRules());
        mongoTemplate.updateFirst(query, update, CityCompany.class);
        String empOrderKey = RedisKeyUtil.getPropertyEmpOrderKey(rulesDto.getCompanyUuid());
        redisUtil.del(empOrderKey);
    }

    @Override
    public String getCompanyUuidByMainCity(String corporationCode, String cityCode) {
        List<CityCompany> companies = repository.findAllByCorporationCodeAndCityCodeAndMainCityFlag(corporationCode, cityCode, 1);
        if (CollectionUtils.isEmpty(companies)) {
            return "";
        }
        return companies.get(0).getCompanyUuid();
    }

    @Override
    public Page<CityCompany> listAllPermissionCityCompany(String corporationCode, List<String> cityCodes, Pageable pageable) {
        Query query = new Query(Criteria.where("corporationCode").is(corporationCode));
        if (!CollectionUtils.isEmpty(cityCodes)) {
            query.addCriteria(Criteria.where("cityCode").in(cityCodes));
        }
        long count = mongoTemplate.count(query, CityCompany.class);
        List<CityCompany> cityCompanies = new ArrayList<>();
        if (count > 0) {
            cityCompanies = mongoTemplate.find(query.with(pageable), CityCompany.class);
        }
        return PageableExecutionUtils.getPage(cityCompanies, pageable, () -> count);
    }

    @Override
    public CityCompany getCityCompany(String webDomain) {
        // 1、从缓存中根据域名获取城市信息
        Object obj = redisUtil.hget(RedisKeyUtil.getWebDomainKey(), webDomain);
        if(obj != null) {
            return JSON.parseObject((String) obj, CityCompany.class);
        }

        // 2、根据域名查询对应的城市
        Query query = new Query(Criteria.where("subWebDomain").is(webDomain).and("status").is(1));
        CityCompany cityCompany = mongoTemplate.findOne(query, CityCompany.class);
        if(cityCompany != null) {
            PropertyPreFilters.MySimplePropertyPreFilter includeFilter = getCityCompanyFilter();
            redisUtil.hset(RedisKeyUtil.getWebDomainKey(), webDomain, JSON.toJSONString(cityCompany, includeFilter));
            return cityCompany;
        }

        // 3、根据域名查询对应的集团（以此判断当前访问的是否是主域名）
        Corporation corporation = corporationRepository.findOneByWebDomain(webDomain);
        if(corporation == null || StringUtils.isEmpty(corporation.getWebDomain())) {
            return null;
        }

        // 4、查询默认城市
        cityCompany = getDefaultCityCompany(corporation.getCorporationCode());
        if (cityCompany != null) {
            PropertyPreFilters.MySimplePropertyPreFilter includeFilter = getCityCompanyFilter();
            redisUtil.hset(RedisKeyUtil.getWebDomainKey(), webDomain, JSON.toJSONString(cityCompany, includeFilter));
        }
        return cityCompany;
    }

    @Override
    public void clearDomainCache(String corporationCode) {
//        Map<Object, Object> domainMap = redisUtil.hmget(RedisKeyUtil.getWebDomainKey());
//        List<String> webDomains = new ArrayList<>();
//        JSONObject jsonObject;
//        for(Map.Entry<Object, Object> entry : domainMap.entrySet()) {
//            jsonObject = JSON.parseObject((String) entry.getValue());
//            if(corporationCode.equals(jsonObject.getString("corporationCode"))) {
//                webDomains.add((String) entry.getKey());
//            }
//        }
//
//        if(!CollectionUtils.isEmpty(webDomains)) {
//            redisUtil.hdel(RedisKeyUtil.getWebDomainKey(), webDomains);
//        }
//        redisUtil.hdel(RedisKeyUtil.getCitysKey(), corporationCode);
        redisUtil.del(RedisKeyUtil.getWebDomainKey(), RedisKeyUtil.getCitysKey());
    }

    /**
     * 获取城市列表
     * @param corporationCode
     * @return
     */
    private List<CityCompany> getCitys(String corporationCode) {
        Object obj = redisUtil.hget(RedisKeyUtil.getCitysKey(), corporationCode);
        if (obj != null) {
            return JSONObject.parseArray((String) obj, CityCompany.class);
        }
        Criteria criteria = Criteria.where("corporationCode").is(corporationCode)
                .and("status").is(1);
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("cityCode").first("cityCode").as("cityCode")
                        .first("companyCity").as("companyCity")
                        .first("corporationCode").as("corporationCode")
                        .first("defaultFlag").as("defaultFlag")
                        .first("companyUuid").as("companyUuid")
                        .first("universeCityId").as("universeCityId")
                        .first("subWebDomain").as("subWebDomain"),
                Aggregation.sort(DESC, "defaultFlag", "cityCode")
        );

        AggregationResults result = mongoTemplate.aggregate(agg, "other_city_company", CityCompany.class);
        List<CityCompany> cityCompanyList = result.getMappedResults();
        if (!CollectionUtils.isEmpty(cityCompanyList)) {
            //第一个城市始终为默认
            cityCompanyList.get(0).setDefaultFlag(1);
            PropertyPreFilters.MySimplePropertyPreFilter includeFilter = getCityCompanyFilter();

            redisUtil.hset(RedisKeyUtil.getCitysKey(), corporationCode, JSON.toJSONString(cityCompanyList, includeFilter));
            for(CityCompany cityCompany : cityCompanyList) {
                redisUtil.hset(RedisKeyUtil.getWebDomainKey(), cityCompany.getSubWebDomain(), JSON.toJSONString(cityCompany, includeFilter));
            }
        }
        return cityCompanyList;
    }

    /**
     * 获取属性过滤
     * @return
     */
    private PropertyPreFilters.MySimplePropertyPreFilter getCityCompanyFilter() {
        PropertyPreFilters.MySimplePropertyPreFilter includeFilter = new PropertyPreFilters().addFilter();
        includeFilter.addIncludes(new String[] {"corporationCode", "cityCode", "companyCity", "companyUuid", "subWebDomain", "defaultFlag", "universeCityId"});
        return includeFilter;
    }

    @Override
    public Boolean subWebDomainIsOnly(CityCompany cityCompanyReq) {
        CityCompany cityCompany = repository.findFirstBySubWebDomain(cityCompanyReq.getSubWebDomain());
        if (cityCompany == null) {
            return false;
        }
        if (org.apache.commons.lang3.StringUtils.equals(cityCompanyReq.getCompanyUuid(), cityCompany.getCompanyUuid())) {
            return false;
        }
        return true;
    }

    @Override
    public List<CityCodeAndNameDto> listCityCodeAndName() {
        String codeListKey = RedisKeyUtil.getCityNameAndCodeListKey();
        Object obj = redisUtil.get(codeListKey);
        if (obj != null) {
            return JSONObject.parseArray(JSONObject.toJSONString(obj), CityCodeAndNameDto.class);
        }

        Query query = new Query(Criteria.where("levelType").is("2"));
        query.fields().include("areaCode").include("shortName");
        List<ProvinceCity> provinceCityList = mongoTemplate.find(query, ProvinceCity.class);

        List<CityCodeAndNameDto> cityCodeAndNameDtoList = Optional.ofNullable(provinceCityList).orElseGet(Lists::newArrayList)
                .stream().sorted(Comparator.comparing(ProvinceCity::getAreaCode))
                .map(provinceCityMapper::domainToDto).filter(f -> org.apache.commons.lang3.StringUtils.isNotBlank(f.getShortName())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(cityCodeAndNameDtoList)){
            return new ArrayList<>();
        }
        redisUtil.set(codeListKey, cityCodeAndNameDtoList, 1, TimeUnit.DAYS);
        return cityCodeAndNameDtoList;
    }

    @Override
    public List<CompanyEmpShowDto> getV20EmpShowList(String corporationCode, List<String> cityCodes) {
        List<CompanyEmpShowDto> results = new LinkedList<>();
        Query query = new Query(Criteria.where("corporationCode").is(corporationCode));
        query.addCriteria(Criteria.where("status").is(1));
        if(!CollectionUtils.isEmpty(cityCodes)){
            query.addCriteria(Criteria.where("cityCode").in(cityCodes));
        }
        List<CityCompany> list = mongoTemplate.find(query, CityCompany.class);
        for (CityCompany cityCompany : list) {
            results.add(CompanyEmpShowDto.builder()
                    .companyCity(cityCompany.getCompanyCity())
                    .companyName(cityCompany.getCompanyName())
                    .companyUuid(cityCompany.getCompanyUuid())
                    .propertyShowRulesStr(getShowRulesStr(cityCompany.getPropertyShowRules()))
                    .build());
        }
        return results;
    }

    private EmpShowRules initCityCompanyEmpShowList(){
        EmpShowRules empShowRules = new EmpShowRules();
        empShowRules.setShowStrategy(1);
        empShowRules.setAssignRule(new AssignRule());
        List<PropertyRole> roleList = new ArrayList<>();
        //这里写死v20的房源角色人
        String[] propertyRoles = {"录入人","维护人","实勘人","委托人","签赔方","钥匙人","VR上传人","视频上传人"};

        for (int i = 0; i < propertyRoles.length; i++) {
            PropertyRole propertyRole = new PropertyRole();
            if (propertyRoles[i].equals("录入人")){
                propertyRole.setFixedShow(true);
                propertyRole.setShowOrHide(true);
            } else if (propertyRoles[i].equals("维护人")){
                propertyRole.setFixedShow(false);
                propertyRole.setShowOrHide(true);
            } else {
                propertyRole.setFixedShow(false);
                propertyRole.setShowOrHide(false);
            }
            propertyRole.setRoleId(Long.valueOf(i));
            propertyRole.setRoleName(propertyRoles[i]);
            propertyRole.setSort(1);
            roleList.add(propertyRole);
        }
        empShowRules.setRoleList(roleList);
        return empShowRules;
    }

}
